
#include <changeloddialog.h>
#include <genericdeletedialog.h>
#include <genericrenamedialog.h>
#include <object_browser/objectbrowserview.h>
#include <scene_editor/scenetreemodel.h>
#include <material_editor/selectmaterialdatadialog.h>
#include <materialdatalistmodel.h>
#include <sceneimporter.h>
#include <sceneloader.h>
#include <scenewriter.h>

#include <gfx/light.h>
#include <manager/materialmanager.h>
#include <scene/light.h>
#include <session.h>
#include <utils/meshcreator.h>
#include <vfs.h>

#include <QFileDialog>
#include <QGridLayout>
#include <QMenu>
#include <QMenuBar>

ObjectBrowserView::ObjectBrowserView(ObjectManager* manager, QWidget* parent)
  : BaseView(parent)
  , _physicsWorld(0)
  , _currentNode(0)
  , _sectorContent(0)
  , _currentObject (0)
  , _manager (manager)
	, _manualSolver (0)
{
  InitGUI();
  InitActions ();
}

void ObjectBrowserView::ConnectSignals()
{
  MainWindow* mw = Session::Get()->GetMainWindow();

  connect (mw->GetAction(MainWindow::Action_Save), SIGNAL(triggered()), this, SLOT(Save ()));
  connect (mw->GetAction(MainWindow::Action_SaveAll), SIGNAL(triggered()), this, SLOT(SaveAll ()));
}

void ObjectBrowserView::CreateMenu(MainWindow *mainWindow)
{
  QMenu* objectsMenu = mainWindow->menuBar()->addMenu("Objects");
  objectsMenu->addAction(_actions[Action_ObjectSelect]);
  objectsMenu->addAction(_actions[Action_ObjectImport]);
  objectsMenu->addAction(_actions[Action_ObjectRename]);
  objectsMenu->addAction(_actions[Action_ObjectRemove]);
  objectsMenu->addSeparator();
  objectsMenu->addAction(_actions[Action_GroupRename]);
  objectsMenu->addAction(_actions[Action_GroupRemove]);

}

void ObjectBrowserView::InitActions()
{
  _actions[Action_ObjectSelect]   = new QAction ("Select", this);
  _actions[Action_ObjectImport]   = new QAction ("Import new object...", this);
  _actions[Action_ObjectRename]   = new QAction ("Rename...", this);
  _actions[Action_ObjectRemove]   = new QAction ("Remove object", this);

  _actions[Action_GroupRename]    = new QAction ("Rename...", this);
  _actions[Action_GroupRemove]    = new QAction ("Remove...", this);

  connect (_actions[Action_ObjectSelect], SIGNAL(triggered()), this, SLOT(ObjectSelect()));
  connect (_actions[Action_ObjectImport],    SIGNAL(triggered()), this, SLOT(ObjectAdd()));
  connect (_actions[Action_ObjectRename], SIGNAL(triggered()), this, SLOT(ObjectRename()));
  connect (_actions[Action_ObjectRemove], SIGNAL(triggered()), this, SLOT(ObjectRemove()));

  connect (_actions[Action_GroupRename],  SIGNAL(triggered()), this, SLOT(GroupRename()));
  connect (_actions[Action_GroupRemove],  SIGNAL(triggered()), this, SLOT(GroupRemove()));


  _nodeActions[NodeAction_Rename] = new QAction ("Rename...", this);
  _nodeActions[NodeAction_ChangeMaterial] = new QAction ("Change Material...", this);
  _nodeActions[NodeAction_ShowMaterial] = new QAction ("Show Material...", this);

  connect (_nodeActions[NodeAction_Rename], SIGNAL(triggered()), this, SLOT(RenameActivated()));
  connect (_nodeActions[NodeAction_ChangeMaterial], SIGNAL(triggered()), this, SLOT(ChangeMaterialActivated()));
  connect (_nodeActions[NodeAction_ShowMaterial], SIGNAL(triggered()), this, SLOT(ShowMaterialActivated()));

}

void ObjectBrowserView::InitGUI()
{
  _splitter = new QSplitter (Qt::Horizontal, this);

  _objectsTree = new QTreeView ();
  _objectsTree->setContextMenuPolicy(Qt::CustomContextMenu);
  _objectsTree->setAllColumnsShowFocus(true);
  _objectsTree->setAnimated(true);
  _objectsTree->setRootIsDecorated(true);
  _objectsTree->setAlternatingRowColors(true);
  _objectsTree->setHeaderHidden(true);

  _nodesTree = new QTreeView ();
  _nodesTree->setContextMenuPolicy(Qt::CustomContextMenu);
  _nodesTree->setRootIsDecorated(true);
  _nodesTree->setAnimated(true);
  _nodesTree->setAlternatingRowColors(true);

  _lodSlider = new QSlider(Qt::Horizontal, this);
  _lodSlider->setEnabled(false);
  _lodSlider->setMinimum(0);
  _lodSlider->setMaximum(0);
  _lodSlider->setValue(0);

  QWidget* wdg = new QWidget (this);
  QGridLayout* glayout = new QGridLayout (wdg);
  glayout->addWidget(_lodSlider, 0, 0, 1, 1);
  glayout->addWidget(_nodesTree, 1, 0, 1, 1);

  _editorComponent = new SceneEditorComponent ();
  _editor = _editorComponent->GetEditor();

  _splitter->addWidget(_objectsTree);
  _splitter->addWidget(_editorComponent);
  _splitter->addWidget(wdg);



  _splitter->setSizes(QList<int>() << width () * 20 / 100 << width () * 40 / 100 <<  width () * 20 / 100);
  _splitter->setStretchFactor(0, 0);
  _splitter->setStretchFactor(1, 1);
  _splitter->setStretchFactor(2, 0);

  _nodesModel = new ObjectOverviewTreeModel();
  _nodesTree->setModel(_nodesModel);

  _objectsModel = new ObjectsTreeModel(_manager);
  _objectsTree->setModel(_objectsModel);

  _editor->setContextMenuPolicy(Qt::CustomContextMenu);

  connect (_objectsTree->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
           this, SLOT(ObjectSelectionChanged (const QModelIndex&, const QModelIndex&)));

  connect (_nodesTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(nodeClicked(const QModelIndex&)));
  connect (_nodesTree, SIGNAL(activated(const QModelIndex&)), this, SLOT(nodeActivated(const QModelIndex&)));
  connect (_nodesTree->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
           this, SLOT(NodeSelectionChanged(const QModelIndex&, const QModelIndex&)));
  connect (_lodSlider, SIGNAL(valueChanged(int)), this, SLOT(LODSliderChanged(int)));

  connect (_objectsTree, SIGNAL(activated(const QModelIndex&)), this, SLOT(objectActivated(const QModelIndex&)));
  connect (_objectsTree, SIGNAL(customContextMenuRequested(const QPoint&)),
           this, SLOT(ObjectContextMenu(const QPoint&)));

  connect (_editor, SIGNAL(Changed()), this, SLOT(EditorChanged()));
  connect (_editor, SIGNAL(NodeSelected(ceNode*)), this, SLOT(SelectOverviewNode(ceNode*)));
  connect (_editor, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ContextMenuRequest(const QPoint&)));


  QGridLayout* layout = new QGridLayout (this);
  layout->addWidget(_splitter, 0, 0, 1, 1);

  _timeFrameWidget = new TimeFrameWidget (this);
  layout->addWidget(_timeFrameWidget, 1, 0, 1, 1);
}

void ObjectBrowserView::Save()
{
  _manager->Save();

  if(!_currentObject)
    {
      return;
    }

  SceneWriter wtr (Session::Get()->GetEngine());


  wtr.WriteNode(_currentNode, Session::Get()->GetProjectFolder() + _currentObject->GetFilename());
}

void ObjectBrowserView::SaveAll()
{
  Save ();
}

void ObjectBrowserView::CreateScene()
{
  if (!_editor->GetScene())
    {
      _editor->MakeGLContextCurrent();
      _editor->CreatePhysicsWorld();
      ceScene* scene = _editor->CreateNewScene();

      ceSector* sectorControls = _editor->CreateControllerSector();

      _sectorContent = new ceSector ();
      _sectorContent->AddRef();

      ceLight* gLight = new ceLight();
      gLight->Ambient = ceColor4f (0.0f, 0.0f, 0.0f);
      gLight->Diffuse = ceColor4f (1.0f, 1.0f, 1.0f);
      gLight->Specular = ceColor4f (1.0f, 1.0f, 1.0f);
      gLight->Range = 1000.0f;
      gLight->RangeMode = LRM_Linear;

      ceLightObject* sLight = new ceLightObject(gLight);
      sLight->SetCastShadow(true);

      ceEntityNode* lightNode = new ceEntityNode();
      lightNode->SetLight(sLight);
      lightNode->SetTranslation(200, 200, 200);
      lightNode->FinishTransformation(true);
      sectorControls->AddNode(lightNode);


      ceMeshCreator creator;
      creator.Initialize(Session::Get()->GetDevice());
      creator.Clear();
      creator.AddVertex(ceVector3f (-100.0f, -100.0f, 0.0f), ceVector3f (0, 0, 1), ceVector2f (0, 0));
      creator.AddVertex(ceVector3f (-100.0f,  100.0f, 0.0f), ceVector3f (0, 0, 1), ceVector2f (0, 1));
      creator.AddVertex(ceVector3f ( 100.0f,  100.0f, 0.0f), ceVector3f (0, 0, 1), ceVector2f (1, 1));
      creator.AddVertex(ceVector3f ( 100.0f, -100.0f, 0.0f), ceVector3f (0, 0, 1), ceVector2f (1, 0));
      creator.AddQuad(0, 1, 2, 3);
      iStaticMesh* mesh = creator.CreateStaticMesh();
      ceGeometry* geom = new ceGeometry(mesh, ceMaterialManager::Get()->GetDefaultMaterial());
      ceEntityNode* node = new ceEntityNode();
      node->SetGeometry(geom);
      node->SetCastShadow(false);
      node->SetTranslation(ceVector3f (0, 0, 0));
      node->FinishTransformation(true);
      sectorControls->AddNode(node);



      scene->AddSector(_sectorContent);
    }

  if (!_physicsWorld)
    {
      _physicsWorld = new cmPhysicsWorld ();
      _physicsWorld->Setup();
    }
}

void ObjectBrowserView::Open(const QString& name)
{
  _editor->ClearPhysicsWorld(true);
  if (_currentNode)
    {
      _sectorContent->RemoveNode(_currentNode);
      CE_UNSET(_currentNode);
    }


  SceneLoader ldr (Session::Get()->GetEngine());

  _currentNode = ldr.Load(Session::Get()->GetProjectFolder() + name);
  if (!_currentNode)
    {
      return;
    }

	CE_UNSET(_manualSolver);
  _lodSlider->blockSignals(true);
  _lodSlider->setMinimum(0);
  _lodSlider->setMaximum(0);
  _lodSlider->setValue(0);
  _lodSlider->setEnabled(false);
  _lodSlider->blockSignals(false);

  _currentNode->AddRef();
  _sectorContent->AddNode(_currentNode);
  _nodesModel->SetShowComposition(false);
  _nodesModel->Build(_sectorContent);

  _editor->InitializePhysics();

  ceScene* scene = _editor->GetScene();
  scene->FindSectorLights ();
  scene->ApplyLights ();
  _editor->repaint();
}


void ObjectBrowserView::ContextMenuRequest (const QPoint &point)
{
  if (!_editor->IsCameraIdle())
    {
      return;
    }
  ceNode* selectedNode = _editor->GetSelectedNode();
  if (!selectedNode)
    {
      return;
    }

  QMenu* menu = new QMenu ();
  menu->addAction(_nodeActions[NodeAction_Rename]);

  if (selectedNode->GetType() == NT_EntityNode && static_cast<ceEntityNode*>(selectedNode)->GetGeometry())
    {
      menu->addAction(_nodeActions[NodeAction_ChangeMaterial]);
      menu->addAction(_nodeActions[NodeAction_ShowMaterial]);
    }

  menu->popup(_editor->mapToGlobal(point));
}


void ObjectBrowserView::MouseMoved(float h, float v)
{
}

void ObjectBrowserView::EditorChanged()
{
}


namespace
{


ObjectOverviewTreeModel::Item* get_node_item (const QModelIndex& index)
{
  if (!index.isValid())
    {
      return 0;
    }

  ObjectOverviewTreeModel::Item* item = static_cast<ObjectOverviewTreeModel::Item*>(index.internalPointer());
  while (item &&  !item->Node)
    {
      item = item->Parent;
    }

  if (!item || !item->Node)
    {
      return 0;
    }
  return item;
}

ObjectOverviewTreeModel::Item* get_item (const QModelIndex& index, ObjectOverviewTreeModel::Item::Type type)
{
  if (!index.isValid())
    {
      return 0;
    }

  ObjectOverviewTreeModel::Item* item = static_cast<ObjectOverviewTreeModel::Item*>(index.internalPointer());
  while (item && item->type != type)
    {
      item = item->Parent;
    }

  if (!item || item->type != type)
    {
      return 0;
    }
  return item;
}

ceNode* get_node (const QModelIndex& index)
{
  ObjectOverviewTreeModel::Item * item = get_node_item (index);
  if (item)
    {
      return item->Node;
    }
  return 0;
}

ceEntityNode* get_entity_node (const QModelIndex& index)
{
  ObjectOverviewTreeModel::Item * item = get_item (index, ObjectOverviewTreeModel::Item::Type_EntityNode);
  if (item)
    {
      return item->entityNode;
    }
  return 0;
}

Material* get_material (const QModelIndex& index)
{
  ObjectOverviewTreeModel::Item * item = get_item (index, ObjectOverviewTreeModel::Item::Type_Material);
  if (item)
    {
      return item->material;
    }
  return 0;
}

}

void ObjectBrowserView::RenameActivated()
{
  ceNode* selectedNode = _editor->GetSelectedNode();
  if (!selectedNode)
    {
      return;
    }

  QString name = selectedNode->GetName().c_str();
  name = GenericRenameDialog::GetName(this, "Rename", "entity", name, true);
  if (name.isNull())
    {
      return;
    }

  selectedNode->SetName(name.toStdString());
}

void ObjectBrowserView::ChangeMaterialActivated ()
{
  ceNode* selectedNode = _editor->GetSelectedNode();
  if (!selectedNode || selectedNode->GetType() != NT_EntityNode)
    {
      return;
    }

  ceEntityNode* entity = static_cast<ceEntityNode*>(selectedNode);

  ceGeometry* geometry = entity->GetGeometry();
  if (!geometry)
    {
      return;
    }
  ObjectOverviewTreeModel::Item *materialItem = _nodesModel->GetMaterialItem(entity);
  if (!materialItem)
    {
      return;
    }


  SelectMaterialDataDialog dlg (this);
  AllMaterialDelegator delegator (Session::Get()->GetMaterialManager());
  dlg.SetDelegator(&delegator);

  if (dlg.exec() != QDialog::Accepted)
    {
      return;
    }

  Geometry* geom = static_cast<Geometry*>(geometry);


  Material* selMaterial = static_cast<Material*>(dlg.GetData());
  materialItem->material = selMaterial;
  geom->SetMetaMaterial(selMaterial);
  _editor->repaint();

}

void ObjectBrowserView::ShowMaterialActivated ()
{
  ceNode* selectedNode = _editor->GetSelectedNode();
  if (!selectedNode || selectedNode->GetType() != NT_EntityNode)
    {
      return;
    }

  ceEntityNode* entity = static_cast<ceEntityNode*>(selectedNode);

  ceGeometry* geometry = entity->GetGeometry();
  if (!geometry)
    {
      return;
    }
  ObjectOverviewTreeModel::Item *materialItem = _nodesModel->GetMaterialItem(entity);
  if (!materialItem)
    {
      return;
    }

  if (materialItem->material)
    {
      Session::Get()->ShowMaterialView(materialItem->material);
    }
}


void ObjectBrowserView::nodeActivated (const QModelIndex& index)
{
  ObjectOverviewTreeModel::Item *materialItem = get_item (index, ObjectOverviewTreeModel::Item::Type_Material);
  if (!materialItem) return;

  ObjectOverviewTreeModel::Item *geometryItem = get_item (index, ObjectOverviewTreeModel::Item::Type_Geometry);
  if (!geometryItem) return;


  ceGeometry* geometry = geometryItem->geometry;

  SelectMaterialDataDialog dlg (this);
  AllMaterialDelegator delegator (Session::Get()->GetMaterialManager()); dlg.SetDelegator(&delegator);

  if (dlg.exec() != QDialog::Accepted)
    {
      return;
    }

  Geometry* geom = static_cast<Geometry*>(geometry);

  Material* selMaterial = static_cast<Material*>(dlg.GetData());
  materialItem->material = selMaterial;
  geom->SetMetaMaterial(selMaterial);
  _editor->repaint();
}

void ObjectBrowserView::nodeClicked(const QModelIndex &index)
{
  ceNode* node = get_node(index);
  _editor->SetSelectedNode(node);
}

void ObjectBrowserView::SelectOverviewNode(ceNode *node)
{
  QModelIndex idx = _nodesModel->GetIndex(node);

  _nodesTree->setCurrentIndex(idx);
}


void ObjectBrowserView::objectActivated(const QModelIndex&)
{
  ObjectSelect();
}

void ObjectBrowserView::LODSliderChanged(int value)
{
  if (!_manualSolver)
    {
      return;
    }

  _manualSolver->SetEntry(value);
  _editor->repaint();

  _editor->ClearPhysicsWorld(false);
  _editor->InitializePhysics();
}

void ObjectBrowserView::NodeSelectionChanged(const QModelIndex &newIndex, const QModelIndex &)
{
  ceLODNode* node = _nodesModel->AsLODNode(newIndex);
  if (!node)
    {

      _lodSlider->blockSignals(true);
      _lodSlider->setEnabled(false);
      _lodSlider->setMinimum(0);
      _lodSlider->setMaximum(0);
      _lodSlider->setValue(0);
      _lodSlider->blockSignals(false);
			CE_UNSET(_manualSolver);
      return;
    }

  LODNode* lodNode = static_cast<LODNode*>(node);
  _manualSolver = lodNode->GetSolver();
	if (_manualSolver)
		{
			_manualSolver->AddRef ();
		}
  _lodSlider->blockSignals(true);
  _lodSlider->setMinimum(0);
  _lodSlider->setMaximum(_manualSolver->GetNum()-1);
  _lodSlider->setValue(_manualSolver->GetEntry());
  _lodSlider->setEnabled(true);
  _lodSlider->blockSignals(false);

}

void ObjectBrowserView::ObjectContextMenu(const QPoint &point)
{

  QMenu* menu = new QMenu (this);
  for (unsigned i=0; i<Action_COUNT; i++)
    {
      if (_actions[i]->isEnabled())
        {
          menu->addAction(_actions[i]);
        }
    }

  menu->popup(_objectsTree->mapToGlobal(point));
}

void ObjectBrowserView::ObjectSelectionChanged(const QModelIndex &newIndex, const QModelIndex&)
{
  for (int i=0; i<Action_COUNT; i++)
    {
      _actions[i]->setEnabled(false);
    }

  // objects are always addable
  _actions[Action_ObjectImport]->setEnabled(true);

  SceneObject* sceneObject = _objectsModel->GetSceneObject(newIndex);
  if (sceneObject)
    {
      _actions[Action_ObjectSelect]->setEnabled(true);
      _actions[Action_ObjectRename]->setEnabled(true);
      _actions[Action_ObjectRemove]->setEnabled(true);
    }

  Category* category = _objectsModel->GetCatetory(newIndex);
  if (category)
    {
      _actions[Action_GroupRename]->setEnabled(true);
      _actions[Action_GroupRemove]->setEnabled(true);
    }
}

void ObjectBrowserView::ObjectSelect()
{
  QModelIndex index = _objectsTree->currentIndex();

  _currentObject = _objectsModel->GetSceneObject(index);
  if (!_currentObject)
    {
      return;
    }

  Open(_currentObject->GetFilename());

}

void ObjectBrowserView::ObjectAdd()
{
  QString selectedFile = QFileDialog::getOpenFileName(this,
                                                      "Import new object",
                                                      Session::Get()->GetProjectFolder() + "/meshes",
                                                      "*.xgroup");
  if (selectedFile.isNull())
    {
      return;
    }

  // use the scene loader to load a part of the
  // file and extract its name
  SceneLoader ldr (Session::Get()->GetEngine());
  QString name = ldr.LoadName(selectedFile);

  QModelIndex index = _objectsTree->currentIndex();
  Category cat;
  if (_objectsModel->IsCategory(index))
    {
      cat = *_objectsModel->GetCatetory(index);
    }

  GenericRenameDialog dlg (this);
  dlg.SetType("New", "scene object");
  dlg.SetCategory(cat, false);
  dlg.SetName(name);
  if (dlg.exec() == QDialog::Rejected)
    {
      return;
    }

  SceneImporter imp;
  selectedFile = imp.ImportGroupNode(selectedFile);
  if (selectedFile.isNull() || selectedFile.length() == 0)
    {
      printf ("selected file is null\n");
      fflush(stdout);
      return;
    }

  cat = dlg.GetCategory();
  name = dlg.GetName();

  SceneObject* sceneObject = new SceneObject ();
  sceneObject->SetCategory(cat);
  sceneObject->SetFilename(selectedFile);
  sceneObject->SetName(name);
  _manager->AddObject(sceneObject);

  Open (selectedFile);
}

void ObjectBrowserView::ObjectRename()
{
  QModelIndex index = _objectsTree->currentIndex();
  SceneObject* object = _objectsModel->GetSceneObject(index);
  if (!object)
    {
      return;
    }

  GenericRenameDialog dlg (this);
  dlg.SetName(object->GetName());
  dlg.SetCategory(object->GetCategory(), false);
  dlg.SetType("Rename", "scene object");
  if (dlg.exec() != QDialog::Accepted)
    {
      return;
    }

  object->SetName(dlg.GetName());
  object->SetCategory(dlg.GetCategory());

  _manager->MoveObject(object);
}

void ObjectBrowserView::ObjectRemove()
{
  QModelIndex index = _objectsTree->currentIndex();
  SceneObject* object = _objectsModel->GetSceneObject(index);
  if (!object)
    {
      return;
    }

  if (GenericDeleteDialog::Delete(this, "scene object",
                                  QString ("Are you sure you want to remove the "
                                           "scene object '%1'?").arg(object->GetName())))
    {
      _manager->RemoveObject(object);
    }
}

void ObjectBrowserView::GroupRename()
{
  QModelIndex index = _objectsTree->currentIndex();
  Category* category = _objectsModel->GetCatetory(index);
  if (!category)
    {
      return;
    }

  Category catOld = *category;

  GenericRenameDialog dlg (this);
  dlg.SetType("Rename", "object category");
  dlg.SetCategory (catOld, false);
  dlg.HideName();

  if (dlg.exec() != QDialog::Accepted)
    {
      return;
    }
  Category catNew = dlg.GetCategory();

  _manager->RenameCategory (catOld, catNew);
}

void ObjectBrowserView::GroupRemove()
{
  QModelIndex index = _objectsTree->currentIndex();
  Category* category = _objectsModel->GetCatetory(index);
  if (!category)
    {
      return;
    }

  Category cat = *category;

  if (GenericDeleteDialog::Delete(this,
                                  "Delete object category",
                                  QString ("Are you sure you want to remove "
                                           "the object category '%1'?").arg (cat.GetFullName())))
    {
      _manager->RemoveCategory (cat);
    }

}
